import {Types} from "mongoose";
import {InstanceType} from 'typegoose'
import {InternalError} from "../../api/error";
import {pressGroupIncomeThreshold, PRICE_2_INCOME} from "../../api/games/pressGame";
import {ConsumeRecordModel} from '../../database/models/consumeRecord';
import {GameActivityModel} from "../../database/models/gameActivity";
import {LipstickSeries, LipstickSeriesModel} from "../../database/models/lipstickSeries";
import {PaymentOrderModel} from "../../database/models/paymentOrder";
import {Player, PlayerModel} from "../../database/models/player";
import {PressGameOrder, PressGameOrderModel} from "../../database/models/pressGameOrder";
import {Product, ProductModel} from "../../database/models/Product";
import {PressGroup, PressGroupIncomeModel, PressGroupModel} from "../../database/models/ProductGroup";
import {wechatPayAuther} from "../../wechat/wechatCashier";
import {expect, requestToApiServer, setupTestEnv, tearDownTestEnv, XMLbuilder} from './helper';

describe('游戏——按的准', () => {

    let player: InstanceType<Player> = null
    let playerPoor: InstanceType<Player> = null
    let jwtPoor: string = null
    let pressGroup: InstanceType<PressGroup> = null
    let jwt: string = null
    let gift: InstanceType<Product> = null

    before(async () => {
      const env = await setupTestEnv()

      player = env.player
      jwt = env.jwt
      gift = env.gift
      pressGroup = env.pressGroup
      playerPoor = env.playerPoor
      jwtPoor = env.jwtPoor
    })

    after(() => {
      return tearDownTestEnv()
    })

    beforeEach(async () => {
      return await GameActivityModel.deleteMany({})
    })

    it('开始一次游戏', async function () {
      const playerBefore = await PlayerModel.findOne({_id: player.id});

      let cost
      let freeCost
      if (pressGroup.priceInCoin < playerBefore.freeCoin) {
        cost = 0
        freeCost = pressGroup.priceInCoin
      } else {
        cost = pressGroup.priceInCoin - playerBefore.freeCoin
        freeCost = playerBefore.freeCoin
      }

      await requestToApiServer()
        .post(`/game/pressAccurateStart/${pressGroup._id}`)
        .set({'x-jwt': jwt})
        .then(async res => {
          expect(res.body).have.properties({ok: true, data: {new: true}})

          const playerAfter = await PlayerModel.findOne({_id: player.id})
          expect(playerAfter.coin).to.equal(playerBefore.coin - cost)
          expect(playerAfter.freeCoin).to.equal(playerBefore.freeCoin - freeCost)
        })

      const act = await GameActivityModel
        .findOne({player: player._id, pressGroup: pressGroup._id})
      expect(act).to.have.properties({state: `free`})

      const gi = await PressGroupIncomeModel.findOne({pressGroup: pressGroup._id})
      expect(gi.income).to.equal(cost)

      const cr = await ConsumeRecordModel.findOne({objectId: pressGroup.id, player: player._id})
      expect(cr.coin).to.equal(cost)
      expect(cr.freeCoin).to.equal(freeCost)
    })

    /*
    it('[接口停用]开始游戏,没有金币,那就付钱(支付宝/微信)，没付返回又点击开始了游戏', async function () {

      let pressGameOrder: InstanceType<PressGameOrder> = null
      await requestToApiServer()
        .post(`/game/pressAccurateStart/${pressGroup._id}`)
        .set({'x-jwt': jwtPoor})
        .then(async res => {
          expect(res.body).have.properties(
            {
              ok: false,
              error: "NO_ENOUGH_COIN"
            })
        })

      await requestToApiServer()
        .post(`/game/pressAccurateRMBStart/${pressGroup._id}`)
        .set({'x-jwt': jwtPoor})
        .send({payType: 'aliPay'})
        .expect(200)

      await requestToApiServer()
        .post(`/game/pressAccurateRMBStart/${pressGroup._id}`)
        .set({'x-jwt': jwtPoor})
        .send({payType: 'wechatPay'})
        .then(({body}) => {
          pressGameOrder = body.data.pressGameOrder
        })

      await requestToApiServer()
        .get(`/game/pressGameOrder/${pressGameOrder._id}`)
        .set({'x-jwt': jwtPoor})
        .then(({body}) => {
          expect(body.ok).to.equal(false)
        })

      await requestToApiServer()
        .post(`/game/pressAccurateStart/${pressGroup._id}`)
        .set({'x-jwt': jwtPoor})
        .then(res => {
          expect(res.body).have.properties(
            {
              ok: false,
              error: "NO_ENOUGH_COIN"
            })
        })
    })
    */
    /*
    it('[接口停用]开始游戏,没有金币,那就付钱(支付宝/微信)', async function () {

      let pressGameOrder: InstanceType<PressGameOrder> = null
      await requestToApiServer()
        .post(`/game/pressAccurateStart/${pressGroup._id}`)
        .set({'x-jwt': jwtPoor})
        .then(async res => {
          expect(res.body).have.properties(
            {
              ok: false,
              error: "NO_ENOUGH_COIN"
            })
        })

      await requestToApiServer()
        .post(`/game/pressAccurateRMBStart/${pressGroup._id}`)
        .set({'x-jwt': jwtPoor})
        .send({payType: 'aliPay'})
        .expect(200)

      await requestToApiServer()
        .post(`/game/pressAccurateRMBStart/${pressGroup._id}`)
        .set({'x-jwt': jwtPoor})
        .send({payType: 'wechatPay'})
        .then(({body}) => {
          pressGameOrder = body.data.pressGameOrder
        })

      const payOrder = await PaymentOrderModel.findOne({
        orderType: 'PressGameOrder', order: pressGameOrder._id
      })

      const notification = {
        out_trade_no: payOrder.id,
        result_code: 'SUCCESS', return_code: 'SUCCESS',
      }
      const sign = wechatPayAuther.sign(notification)
      const xml = XMLbuilder.buildObject({...notification, sign})
      const res = await requestToApiServer()
        .post('/wechatpay/notify')
        .type('xml')
        .send(xml)
        .expect(200)
        .then(res => {
          expect(res.text).to.equal('<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>')
        })

      expect(await PaymentOrderModel.findOne({
        orderType: 'PressGameOrder', order: pressGameOrder._id
      })).to.have.properties({state: 'finished'})

      expect(await PressGameOrderModel.findById(pressGameOrder._id).lean())
        .to.have.properties({state: 'paid'})

      const act = await GameActivityModel
        .findOne({player: playerPoor._id, pressGroup: pressGroup._id})
      expect(act).to.have.properties({isPayRMB: true})

      let activity = null
      await requestToApiServer()
        .get(`/game/pressGameOrder/${pressGameOrder._id}`)
        .set({'x-jwt': jwtPoor})
        .then(({body}) => {
          activity = body.data.activity
        })
      const pgo = await PressGameOrderModel.findById(pressGameOrder)
        .populate(`activity`).lean()
      expect(pgo.activity.winLevel).to.equal(activity.winLevel)

      const pr = await PlayerModel.findOne({_id: playerPoor.id});
      expect(pr.coin).to.equal(0)
      expect(pr.freeCoin).to.equal(0)
    })
    */

    it('第一次游戏未结束，start 继续上局游戏信息', async function () {

      let currentActivityData
      await requestToApiServer()
        .post(`/game/pressAccurateStart/${pressGroup._id}`)
        .set({'x-jwt': jwt})
        .then(res => {
          expect(res.body).to.have.properties({ok: true})
          currentActivityData = res.body.data
        })

      await requestToApiServer()
        .post(`/game/pressAccurateStart/any_pressGroup_id`)
        .set({'x-jwt': jwt})
        .then(res => {
          expect(res.body).to.have.properties({
            ok: true, data: {
              ...currentActivityData, new: false
            }
          })
        })
    })

    it('上报游戏结束——失败且没有gift奖', async function () {

      let currentActivityData = null
      await requestToApiServer()
        .post(`/game/pressAccurateStart/${pressGroup._id}`)
        .set({'x-jwt': jwt})
        .then(res => {
          expect(res.body).to.have.properties({ok: true})
          currentActivityData = res.body.data
        })

      await requestToApiServer()
        .post(`/game/pressAccurateClaim/${currentActivityData.activity}`)
        .set({'x-jwt': jwt})
        .send({winLevel: 0})
        .expect(200)
        .then(res => {
          expect(res.body).to.have.properties({
            ok: true, data: {
              winLevel: 0,
            }
          })
        })

      const actModel = await GameActivityModel.findById(currentActivityData.activity)
      expect(actModel).to.have.properties({state: 'done'})
    })

    it('上报游戏结束后，可以开始新游戏', async function () {

      let currentActivityData = null
      await requestToApiServer()
        .post(`/game/pressAccurateStart/${pressGroup._id}`)
        .set({'x-jwt': jwt})
        .then(res => {
          expect(res.body).to.have.properties({ok: true})
          currentActivityData = res.body.data
        })

      await requestToApiServer()
        .post(`/game/pressAccurateClaim/${currentActivityData.activity}`)
        .set({'x-jwt': jwt})
        .send({winLevel: 0})
        .expect(200)
        .then(res => {
          expect(res.body).to.have.properties({
            ok: true, data: {
              winLevel: 0
            }
          })
        })

      await requestToApiServer()
        .post(`/game/pressAccurateStart/${pressGroup._id}`)
        .set({'x-jwt': jwt})
        .then(res => {
          expect(res.body).to.have.properties({ok: true, data: {new: true}})
        })
    })

    it('开始一次游戏 有奖', async function () {

      await PressGroupIncomeModel.findOneAndUpdate({pressGroup: pressGroup._id},
        {
          $set: {income: 10000}
        })

      await requestToApiServer()
        .post(`/game/pressAccurateStart/${pressGroup._id}`)
        .set({'x-jwt': jwt})
        .then(async res => {
          expect(res.body).have.properties({ok: true, data: {new: true}})

          expect(1).to.lessThan(res.body.data.winLevel)
        })
    })

    context('当前游戏查询', async () => {

      it('当前无游戏', async () => {
        await requestToApiServer()
          .get(`/game/my/activity/pressAccurate`)
          .set({'x-jwt': jwt})
          .then(res => {
            expect(res.body).to.have.properties({
              ok: true, data: {activity: null}
            })
          })
      })

      it('当前有游戏', async () => {

        let activity = ''
        await requestToApiServer()
          .post(`/game/pressAccurateStart/${pressGroup._id}`)
          .set({'x-jwt': jwt})
          .then(res => {
            expect(res.body.ok).to.equal(true)
            activity = res.body.data.activity
          })

        await requestToApiServer()
          .get(`/game/my/activity/pressAccurate`)
          .set({'x-jwt': jwt})
          .then(res => {
            expect(res.body).to.have.properties({
              ok: true, data: {activity: {_id: activity}}
            })
          })
      })
    })

    context('游戏结束库存大盘测试', async () => {

      let act = null
      const productStockBefore = {stock1: 0, stock2: 0, stock3: 0}
      beforeEach(async () => {
        productStockBefore.stock1 = await getLevelStocksInPressGame(pressGroup.id, 1)
        productStockBefore.stock2 = await getLevelStocksInPressGame(pressGroup.id, 2)
        productStockBefore.stock3 = await getLevelStocksInPressGame(pressGroup.id, 3)
        const {body} = await requestToApiServer()
          .post(`/game/pressAccurateStart/${pressGroup._id}`)
          .set({'x-jwt': jwt})
          .expect(200)
          .send()

        act = body.data.activity
      })

      it('和预期相同 —— level=3', async () => {
        const preLevel = 3
        const pgoBefore = await PressGroupIncomeModel.findOne({pressGroup: pressGroup._id})
        const threshold = await pressGroupIncomeThreshold(pressGroup._id)

        await GameActivityModel.updateOne({_id: act}, {
          $set: {
            state: 'free',
            winLevel: preLevel,
            income: threshold[`income${preLevel}`]
          }
        })
        await requestToApiServer()
          .post(`/game/pressAccurateClaim/${act}`)
          .set({'x-jwt': jwt})
          .expect(200)
          .send({winLevel: preLevel})
          .then(({body}) => {
            expect(body).to.have.properties({data: {winLevel: preLevel}})
          })
        const productStockAfter = {stock1: 0, stock2: 0, stock3: 0}
        productStockAfter.stock1 = await getLevelStocksInPressGame(pressGroup.id, 1)
        productStockAfter.stock2 = await getLevelStocksInPressGame(pressGroup.id, 2)
        productStockAfter.stock3 = await getLevelStocksInPressGame(pressGroup.id, 3)
        expect(productStockBefore[`stock${preLevel}`] - 1).to.equal(productStockAfter[`stock${preLevel}`])

        const pgoAfter = await PressGroupIncomeModel.findOne({pressGroup: pressGroup._id})
        expect(pgoAfter.income).to.equal(pgoBefore.income)
      })
      it('和预期相同 —— level=2', async () => {
        const preLevel = 2
        const pgoBefore = await PressGroupIncomeModel.findOne({pressGroup: pressGroup._id})
        const threshold = await pressGroupIncomeThreshold(pressGroup._id)

        await GameActivityModel.updateOne({_id: act}, {
          $set: {
            state: 'free',
            winLevel: preLevel,
            income: threshold[`income${preLevel}`]
          }
        })
        await requestToApiServer()
          .post(`/game/pressAccurateClaim/${act}`)
          .set({'x-jwt': jwt})
          .expect(200)
          .send({winLevel: preLevel})
          .then(({body}) => {
            expect(body).to.have.properties({data: {winLevel: preLevel}})
          })
        const productStockAfter = {stock1: 0, stock2: 0, stock3: 0}
        productStockAfter.stock1 = await getLevelStocksInPressGame(pressGroup.id, 1)
        productStockAfter.stock2 = await getLevelStocksInPressGame(pressGroup.id, 2)
        productStockAfter.stock3 = await getLevelStocksInPressGame(pressGroup.id, 3)
        expect(productStockBefore[`stock${preLevel}`] - 1).to.equal(productStockAfter[`stock${preLevel}`])

        const pgoAfter = await PressGroupIncomeModel.findOne({pressGroup: pressGroup._id})
        expect(pgoAfter.income).to.equal(pgoBefore.income)
      })
      it('和预期相同 —— level=1', async () => {
        const preLevel = 1
        const pgoBefore = await PressGroupIncomeModel.findOne({pressGroup: pressGroup._id})
        const threshold = await pressGroupIncomeThreshold(pressGroup._id)

        await GameActivityModel.updateOne({_id: act}, {
          $set: {
            state: 'free',
            winLevel: preLevel,
            income: threshold[`income${preLevel}`]
          }
        })
        await requestToApiServer()
          .post(`/game/pressAccurateClaim/${act}`)
          .set({'x-jwt': jwt})
          .expect(200)
          .send({winLevel: preLevel})
          .then(({body}) => {
            expect(body).to.have.properties({data: {winLevel: preLevel}})
          })
        const productStockAfter = {stock1: 0, stock2: 0, stock3: 0}
        productStockAfter.stock1 = await getLevelStocksInPressGame(pressGroup.id, 1)
        productStockAfter.stock2 = await getLevelStocksInPressGame(pressGroup.id, 2)
        productStockAfter.stock3 = await getLevelStocksInPressGame(pressGroup.id, 3)
        expect(productStockBefore[`stock${preLevel}`] - 1).to.equal(productStockAfter[`stock${preLevel}`])

        const pgoAfter = await PressGroupIncomeModel.findOne({pressGroup: pressGroup._id})
        expect(pgoAfter.income).to.equal(pgoBefore.income)
      })

      it('和预期相同 —— 无奖', async () => {

        const preLevel = 0
        const pgoBefore = await PressGroupIncomeModel.findOne({pressGroup: pressGroup._id})
        const threshold = await pressGroupIncomeThreshold(pressGroup._id)

        await GameActivityModel.updateOne({_id: act}, {
          $set: {
            state: 'free',
            winLevel: preLevel,
            income: threshold[`income${preLevel}`]
          }
        })
        await requestToApiServer()
          .post(`/game/pressAccurateClaim/${act}`)
          .set({'x-jwt': jwt})
          .expect(200)
          .send({winLevel: preLevel})
          .then(({body}) => {
            expect(body).to.have.properties({data: {winLevel: preLevel}})
          })

        const pgoAfter = await PressGroupIncomeModel.findOne({pressGroup: pressGroup._id})
        expect(pgoAfter.income).to.equal(pgoBefore.income)

      })

      it('和预期不相同 —— 合规', async () => {
        const pgoBefore = await PressGroupIncomeModel.findOne({pressGroup: pressGroup._id})
        const threshold = await pressGroupIncomeThreshold(pressGroup._id)

        const ac = await GameActivityModel.findById(act)
        const winLevel = ac.winLevel
        ac.income = threshold[`income${winLevel}`]
        await ac.save()
        await requestToApiServer()
          .post(`/game/pressAccurateClaim/${act}`)
          .set({'x-jwt': jwt})
          .expect(200)
          .send({winLevel: 1})
          .then(({body}) => {
            expect(body).to.have.properties({data: {winLevel: 1}})
          })

        const productStockAfter = {stock1: 0, stock2: 0, stock3: 0}
        productStockAfter.stock1 = await getLevelStocksInPressGame(pressGroup.id, 1)
        productStockAfter.stock2 = await getLevelStocksInPressGame(pressGroup.id, 2)
        productStockAfter.stock3 = await getLevelStocksInPressGame(pressGroup.id, 3)
        expect(productStockBefore[`stock${1}`] - 1).to.equal(productStockAfter[`stock${1}`])
        expect(productStockBefore[`stock${2}`]).to.equal(productStockAfter[`stock${2}`])
        expect(productStockBefore[`stock${3}`]).to.equal(productStockAfter[`stock${3}`])

        const pgoAfter = await PressGroupIncomeModel.findOne({pressGroup: pressGroup._id})
        expect(pgoAfter.income).to.equal(pgoBefore.income + threshold[`income${winLevel}`] - threshold[`income${1}`])
      })

      it('和预期不相同 —— 不合规', async () => {
        const pgoBefore = await PressGroupIncomeModel.findOne({pressGroup: pressGroup._id})
        const threshold = await pressGroupIncomeThreshold(pressGroup._id)

        const ac = await GameActivityModel.findById(act)
        const winLevel = ac.winLevel
        ac.income = threshold[`income${winLevel}`]
        await ac.save()
        await requestToApiServer()
          .post(`/game/pressAccurateClaim/${act}`)
          .set({'x-jwt': jwt})
          .expect(200)
          .send({winLevel: 3})
          .then(({body}) => {
            expect(body).to.have.properties({ok: false})
          })

        const productStockAfter = {stock1: 0, stock2: 0, stock3: 0}
        productStockAfter.stock1 = await getLevelStocksInPressGame(pressGroup.id, 1)
        productStockAfter.stock2 = await getLevelStocksInPressGame(pressGroup.id, 2)
        productStockAfter.stock3 = await getLevelStocksInPressGame(pressGroup.id, 3)
        expect(productStockBefore[`stock${1}`] - 1).to.equal(productStockAfter[`stock${1}`])
        expect(productStockBefore[`stock${2}`] - 1).to.equal(productStockAfter[`stock${2}`])
        expect(productStockBefore[`stock${3}`] - 1).to.equal(productStockAfter[`stock${3}`])

        const pgoAfter = await PressGroupIncomeModel.findOne({pressGroup: pressGroup._id})
        expect(pgoAfter.income).to.equal(pgoBefore.income + threshold[`income${winLevel}`])
      })

    })

    async function getLevelStocksInPressGame(id: Types.ObjectId, num: number):
      Promise<number> {
      const pg = await PressGroupModel.findById(id)
        .populate(`level${num}`)

      let stock = 0
      for (const lip of pg[`level${num}`]) {
        const pros = (await LipstickSeriesModel.findById(lip._id).populate(`products`)).products
        for (const it of pros as Array<InstanceType<Product>>) {
          stock += it.stock
        }
      }

      return stock
    }
  }
)
